package agendaDOM_JAXB;

/**
 * It is responsible for implementing methods of Agenda with JAXB.
 * @author <A HREF="fernandezlopez.raul@gmail.com">Ra�l Fern�ndez L�pez</A> 
 * @version 1.00
 */

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.swing.DefaultListModel;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import agendaInterfaz.MainWindow;

import es.ubu.ceudaaj.exercise.ud02.t04.l03.model.Contactos;
import es.ubu.ceudaaj.exercise.ud02.t04.l03.model.Dataroot;

/**
 * Class representing a list of contacts that can be read and written into and from an xml file.
 * 
 * This version uses JAXB to process the xml.
 * 
 */
public class AgendaJAXB implements Agenda{
    
    private static final String XML_INPUT_PATH = "src/main/resources/xml/Contactos.xml";
    private static final String XML_OUTPUT_PATH = "src/main/resources/xml/Contactos_new.xml";
    
    /**
     *  Path to the JAXB classes (that have been generated first, by using 'jxc')
     */
    private static final String JAXB_PACKAGE = "es.ubu.ceudaaj.exercise.ud02.t04.l03.model";
    
    /**
     *  Attribute to keep a reference to the current XML in memory.
     */
    private Dataroot dataroot = null;
    
    /**
     * Atribute to keep the instance of AgendaJAXB.
     */
    private static AgendaJAXB instance= null;
    
    /**
     * Atribute to keep a list of contacts.
     */
    private DefaultListModel<String> modelo;
    
    /**
     * Attribute to keep the main window.
     */
    private MainWindow win; 
    
    /**
     * Private constructor
     * This class should only be used as singleton
     */
    private AgendaJAXB(){}
    
    /**
     * Returns a JAXB based agenda
     * @return AgendaJAXB
     */
    public static AgendaJAXB getInstance(){
    	if(instance==null) instance = new AgendaJAXB();
    	return instance;
    }
    

    /**
     * Shows a list of current contacts on console. (Only name and surnames).
     * 
     * @return modelo List of Contacts.
     */
    public DefaultListModel<String> listContacts() {
        int index = 0;
        modelo = new DefaultListModel<String>();
        
        if (dataroot != null && !dataroot.getContactos().isEmpty()) {
            for (Contactos contact : dataroot.getContactos()) {
                index ++;
                modelo.addElement(index + ". " 
                        + contact.getApellidos() + ", " + contact.getNombre());
            }
        } else {
            System.out.println("(lista vacia)");
        }
        
        return modelo;
    }
    
    /**
     * Prints detailed information of selected user on console.
     * 
     * @param index Position of selected contact.
     * @return a string with details of a contact.
     * @throws IOException
     */
    public String printContactDetail(int index) throws IOException {
    	StringBuilder sb=null;
		if (dataroot != null && dataroot.getContactos() != null
				&& dataroot.getContactos().size() >= index && index > 0) {
			Contactos cont = dataroot.getContactos().get(index - 1);
			sb = new StringBuilder("Detalle del contacto " + index + ": ");
			sb.append("\n\tID: ").append(cont.getIdContacto());
			sb.append("\n\tNombre: ");
			if (cont.getEstimado() != null) {
				sb.append(cont.getEstimado()).append(" ");
			}
			if (cont.getNombre() != null) {
				sb.append(cont.getNombre()).append(" ");
			}
			if (cont.getApellidos() != null) {
				sb.append(cont.getApellidos());
			}
			if (cont.getDireccion() != null) {
				sb.append("\n\tDireccion: ");
				sb.append(cont.getDireccion());
			}
			if (cont.getCodPostal() != null) {
				sb.append(", ").append(cont.getCodPostal());
			}
			if (cont.getCiudad() != null) {
				sb.append(", ").append(cont.getCiudad());
			}
			if (cont.getPais() != null) {
				sb.append(", ").append(cont.getPais());
			}
			if (cont.getCargo() != null) {
				sb.append("\n\tCargo: ").append(cont.getCargo());
			}
			if (cont.getTelefonoTrabajo() != null) {
				sb.append("\n\tTfno. trabajo: ").append(cont.getTelefonoTrabajo());
			}
			if (cont.getExtensionTrabajo() != null) {
				sb.append("\n\tExt. trabajo: ").append(cont.getExtensionTrabajo());
			}
			if (cont.getNumFax() != null) {
				sb.append("\n\tFax: ").append(cont.getNumFax());
			}
			if (cont.getNomCorreoElectronico() != null) {
				sb.append("\n\tEmail: ").append(cont.getNomCorreoElectronico());
			}
			System.out.println(sb);
        } else {
            System.out.println("No existe en la lista de contactos.");
        }
		return sb.substring(0);
    }

    /**
     * Creates a new contact and adds it to the XML representation in memory.
     * 
     * @throws IOException
     */
        public void createNewContact() throws IOException {
        
        if (dataroot == null) {
        	dataroot = new Dataroot();
        	GregorianCalendar c = new GregorianCalendar();
        	c.setTime(new Date());
        	XMLGregorianCalendar xmlgc;
			try {
				xmlgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
				dataroot.setGenerated(xmlgc);
			} catch (DatatypeConfigurationException e) {
				System.err.println("Error al crear nuevo contacto: " + e);
				return;
			}
        }
        
        win = new MainWindow();       
        Contactos cont = new Contactos();
        cont.setIdContacto(dataroot.getContactos().size() + 1);
        cont.setNombre(win.getFrame().getTxtNombre().getText());
        cont.setApellidos(win.getFrame().getTxtApellido().getText());
        cont.setTelefonoTrabajo(win.getFrame().getTxtTelefono().getText());
        cont.setNomCorreoElectronico(win.getFrame().getTxtMail().getText());
                
        dataroot.getContactos().add(cont);
    }

    /**
     * Method for editing the information for the selected contact in the XML representation in memory.
     * 
     * @param index Position of selected contact.
     * @return true if the contact is change correctly.
     * @throws IOException
     */
        public boolean editContact(int index) throws IOException {
       
        win = new MainWindow();
      
    	if (dataroot != null && dataroot.getContactos() != null 
                && dataroot.getContactos().size() >= index && index > 0) {
    		
    		Contactos contacto = dataroot.getContactos().get(index - 1);

    		contacto.setNombre(win.getFrame().getTxtNombre().getText());
    		contacto.setApellidos(win.getFrame().getTxtApellido().getText());
    		contacto.setTelefonoTrabajo(win.getFrame().getTxtTelefono().getText());
    		contacto.setNomCorreoElectronico(win.getFrame().getTxtMail().getText());

    		System.out.println("Se ha editado correctamente el contacto");
    		return true;
    	} else {
            System.out.println("No existe en la lista de contactos.");
        }
    	return false;
    }

    /**
     * Removes the selected contact from the XML representation in memory.
     * 
     * @param index Position of selected contact.
     */
    public void deleteContact(int index) {
        if (dataroot != null && dataroot.getContactos() != null 
                && dataroot.getContactos().size() >= index) {
            dataroot.getContactos().remove(index - 1);
            System.out.println("Contacto borrado.");
        } else {
            System.out.println("No existe en la lista de contactos.");
        }
    }

    /**
     * Writes the current XML in a file.
     * 
     * @return message after save a contact.
     */

	public String saveXML() {
    	String message="";
    	
    	if (dataroot != null){
	        try {
	            JAXBContext jc = JAXBContext.newInstance(JAXB_PACKAGE);
	            Marshaller marshaller = jc.createMarshaller();
	            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
	            marshaller.marshal(dataroot, new File(XML_OUTPUT_PATH));
	            message="Se ha guardado correctamente el fichero " + XML_OUTPUT_PATH;
	        } catch (JAXBException ex) {
	        	message="Error guardando el fichero XML: " + ex;
	        }
    	} else {
    		message="Aun no existe ningun contacto";
    	}
    	return message;
    }

    /**
     * Reads the input XML file.
     */

	public void loadXML() {
        try {
            JAXBContext jc = JAXBContext.newInstance(JAXB_PACKAGE);
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            dataroot = (Dataroot)unmarshaller.unmarshal(new File(XML_INPUT_PATH));
            System.out.println("Se ha leido el fichero " + XML_INPUT_PATH);
        } catch (JAXBException ex) {
            System.err.println("Error al obtener los contactos del XML: " + ex);
        }
    }
        
    /**
     * Search contacts with the given name in the XML in memory.
     * 
     * Uses XPath for the search.
     *  
     * @param name Name of the contact to search.
     * @return list that contains the contacts of search by name. 
     */

    public List<String> searchByName(String name) {
    	int resultados = 0;
    	List<String> results = new ArrayList<String>();
    	
        for ( Contactos contact : dataroot.getContactos()) {
        	if (contact.getNombre() != null && contact.getNombre().equalsIgnoreCase(name)) {
        		resultados ++;
        		results.add(contact.getIdContacto() + "- " + contact.getNombre() + " " + contact.getApellidos());
        	}
		}
        if (resultados <= 0){
    		System.out.println("No se han encontrado resultados.");
    	}
        return results;
    }

    /**
     * Search contacts with the given job in the XML in memory.
     * 
     * Uses XPath for the search.
     *  
     * @param job Job of the contact to search.
     * @return list that contains the contacts of search by job. 
     */

    public List<String> searchByJob(String job) {
    	int resultados = 0;
    	List<String> results = new ArrayList<String>();
        for ( Contactos contact : dataroot.getContactos()) {
        	if (contact.getCargo() != null && contact.getCargo().equalsIgnoreCase(job)) {
        		resultados ++;
        		results.add(contact.getIdContacto()+ "- " + contact.getNombre() + " " + contact.getApellidos());
        	}
		}
        if (resultados <= 0){
    		System.out.println("No se han encontrado resultados.");
    	}
        
        return results;
    }

    
    /**
     * Search contacts with the given postal code in the XML in memory.
     * 
     * Uses XPath for the search.
     *  
     * @param postalCode Postal code of the contact to search.
     * @return list that contains the contacts of search by postal code. 
     */

    public List<String> searchByPostalCode(String postalCode) {
    	int resultados = 0;
    	List<String> results = new ArrayList<String>();
        for ( Contactos contact : dataroot.getContactos()) {
        	if (contact.getCodPostal() != null && contact.getCodPostal().equalsIgnoreCase(postalCode)) {
        		resultados ++;
        		results.add(contact.getIdContacto()+ "- " +contact.getNombre() + " " + contact.getApellidos());
        	}
		}
        if (resultados <= 0){
    		System.out.println("No se han encontrado resultados.");
    	}
        return results;
    }

    
    /**
     * Method that returns size of List of contacts.
     * 
     * @return size of List of contacts. 
     */

	public int getNumContactos() {
		return modelo.size();
	}

	/**
     * Prepares a query to evaluate with XPath and executes the query.
     * 
     * @param predicate The predicate specifying the specific search to do.
     * @return list that contains the contacts of search by predicate. 
     */

	public List<String> xpathSearch(String predicate) {
		return null;
	}

	/**
     * Shows a list of current contacts on console. (Only name and surnames).
     * 
     * @param idContacto ID of a contact.
     */

	public void searchContactLostfocus(int idContacto) {
    	win = new MainWindow();
    	boolean encontrado = false;
    	
    	for ( Contactos contact : dataroot.getContactos()) {
        	if (contact.getNombre() != null && contact.getIdContacto()==idContacto) {
        		win.getFrame().getTxtNombre().setText(contact.getNombre());
				win.getFrame().getTxtApellido().setText(contact.getApellidos());
				win.getFrame().getTxtTelefono().setText(contact.getExtensionTrabajo());
				win.getFrame().getTxtMail().setText(contact.getNomCorreoElectronico());
				encontrado= true;
        	}
		}
    	if (!encontrado){
			win.getFrame().getTxtNombre().setText("");
			win.getFrame().getTxtApellido().setText("");
			win.getFrame().getTxtTelefono().setText("");
			win.getFrame().getTxtMail().setText("");
		}
    }   
}